import { RoomCallBack } from '@/ayaka'
import { goTo } from '@/modules/move'
import { isNearEdge } from '@/utils/position'
import { SquadTask } from '../types'

/**
 * 四人小队移动
 */
export function squadMove(
    creeps: Creep[],
    task: SquadTask,
    standPos: RoomPosition | null,
    targetPos: RoomPosition,
    range = 1,
    flee = false,
) {
    if (task.formation === 'line') {
        lineMove(creeps, targetPos, range, flee)
    } else {
        const result = squareMove(creeps, standPos, targetPos, range, flee)
        // 不可达就改成线性移动
        if (result === ERR_NO_PATH) {
            task.formation = 'line'
        }
    }
}

/**
 * 线性移动
 */
export function lineMove(creeps: Creep[], targetPos: RoomPosition, range = 1, flee = false) {
    creeps.forEach((creep, index) => {
        const prevCreep = creeps[index - 1]
        const nextCreep = creeps[index + 1]
        // 不同房间停下来等待，或者发现后一个爬掉队了，也停下来等待
        if (
            nextCreep &&
            !isNearEdge(creep.pos, 0) &&
            (creep.room !== nextCreep.room || !creep.pos.isNearTo(nextCreep))
        ) {
            return
        }

        if (prevCreep) {
            if (creep.pos.isNearTo(prevCreep)) {
                creep.move(creep.pos.getDirectionTo(prevCreep))
            } else {
                creep.moveTo(prevCreep)
            }
            return
        }

        // 第一个爬主动寻路
        goTo(creep, targetPos, { range, maxOps: 8000, flee })
    })
}

/**
 * 批量添加 costs
 */
function addCosts(costs: CostMatrix, x: number, y: number, value: number) {
    costs.set(x, y, value)
    costs.set(x - 1, y, value)
    costs.set(x, y - 1, value)
    costs.set(x - 1, y - 1, value)
}

/**
 * 正方形移动
 */
export function squareMove(
    creeps: Creep[],
    standPos: RoomPosition | null,
    targetPos: RoomPosition,
    range = 1,
    flee = false,
) {
    if (!standPos) return

    for (const c of creeps) {
        /* 如果有疲劳单位，就停止 */
        if (c.fatigue) return ERR_TIRED
        /* 如果没有腿子,就停止 */
        if (c.getActiveBodyparts('move') <= 0) return ERR_NO_BODYPART
    }

    const roomCallback: RoomCallBack = (roomName) => {
        // 在绕过房间列表的房间将直接不让走
        if (Memory.bypassRooms && Memory.bypassRooms.includes(roomName)) return false

        const room = Game.rooms[roomName]
        const costs = new PathFinder.CostMatrix()
        const terrian = new Room.Terrain(roomName)
        // 先设置沼泽的 cost
        for (let x = 0; x < 50; x++) {
            for (let y = 0; y < 50; y++) {
                if (terrian.get(x, y) == TERRAIN_MASK_SWAMP) {
                    addCosts(costs, x, y, 10)
                }
            }
        }
        // 再设置墙壁的 cost
        for (let x = 0; x < 50; x++) {
            for (let y = 0; y < 50; y++) {
                if (terrian.get(x, y) == TERRAIN_MASK_WALL) {
                    addCosts(costs, x, y, 255)
                }
            }
        }

        if (!room) return

        // 无法行走的建筑设置为 255
        room.find(FIND_STRUCTURES).forEach((struct) => {
            if (
                struct.structureType !== STRUCTURE_CONTAINER &&
                struct.structureType !== STRUCTURE_ROAD &&
                (struct.structureType !== STRUCTURE_RAMPART || !struct.my)
            ) {
                addCosts(costs, struct.pos.x, struct.pos.y, 255)
            }
        })

        if (standPos.roomName == room.name) {
            room.find(FIND_MY_CREEPS).forEach((creep) => {
                // 只要不是四人小队的爬都设置成 255
                if (!creeps.includes(creep)) {
                    addCosts(costs, creep.pos.x, creep.pos.y, 255)
                }
            })
        }
        return costs
    }

    const result = PathFinder.search(
        standPos,
        { pos: targetPos, range: range },
        {
            plainCost: 2,
            swampCost: 10,
            maxOps: 8000,
            // @ts-ignore
            roomCallback: roomCallback,
            // 在敌方房间并且不是逃跑状态就只待在那个房间
            maxRooms: standPos.roomName === targetPos.roomName && !flee ? 1 : 20,
            flee,
        },
    )

    if (result.path.length <= 0) return ERR_NO_PATH

    const direction = standPos.getDirectionTo(result.path[0])
    if (!direction) return

    for (const creep of creeps) {
        creep.move(direction)
    }

    // 比直接距离还短，说明不可达
    if (standPos.roomName === targetPos.roomName && result.path.length < standPos.getRangeTo(targetPos) - range) {
        return ERR_NO_PATH
    }
    return OK
}
